package com.ruoyi.combat.repository;

import com.ruoyi.combat.domain.neo4j.CombatQueryInfo;
import com.ruoyi.combat.domain.neo4j.IpNode;
import com.ruoyi.combat.domain.neo4j.OrgNode;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.query.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

@Repository
public interface IpNodeRepository extends Neo4jRepository<IpNode, Long> {

    @Query(value = "MATCH " +
            "(startNode)-[r1:BELONGS_TO]->(endNode) " +
            "RETURN startNode, r1, endNode " +
            "SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH " +
                    "(startNode)-[r1:BELONGS_TO]->(endNode) " +
                    "RETURN count(endNode)")
    Page<IpNode> findIPToOrgByType(@Param("type") String type, @Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize, Pageable pageable);

    /**
     * 统计有BELONGS_TO关系的IP数量
     */
    @Query(value = "MATCH (n:Ip)-[r1:BELONGS_TO]->(m) RETURN count(m)")
    Long countByBelongsTo();

    /**
     * 分页查询节点
     */
    @Query(value = "MATCH (n:Ip) RETURN n SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (n:Ip) RETURN count(n)")
    Page<IpNode> findByPage(@Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize, Pageable pageable);

    /**
     * 分页查询关系
     */
    @Query(value = "MATCH (startNode)-[r1:BELONGS_TO]->(endNode1) " +
            "RETURN startNode, r1, endNode1 " +
            "SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (startNode)-[r1:BELONGS_TO]->(endNode1) " +
                    "RETURN count(r1)")
    Page<IpNode> findRelationsByPage(@Param("pageNum") Integer pageNum,
                                     @Param("pageSize") Integer pageSize, Pageable pageable);

    /**
     * 根据IP分页查询图谱关系
     */
    @Query(value = "MATCH (n:Ip) " +
            "WHERE n.Ip = $ip " +
            "RETURN n " +
            "SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (n:Ip) RETURN count(n)")
    Page<IpNode> findIPNodeByIp(@Param("ip") String ip,
                                @Param("pageNum") Integer pageNum,
                                @Param("pageSize") Integer pageSize, Pageable pageable);

    /**
     * 根据组织分页查询图谱关系
     */
    @Query(value = "MATCH " +
            "(startNode)-[r1:BELONGS_TO]->(endNode1) " +
            "WHERE endNode1.name = $org " +
            "RETURN startNode, r1, endNode1 " +
            "SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (startNode)-[r1:BELONGS_TO]->(endNode1) " +
                    "RETURN count(r1)")
    Page<IpNode> findRelationsByOrg(@Param("org") String org,
                                    @Param("pageNum") Integer pageNum,
                                    @Param("pageSize") Integer pageSize, Pageable pageable);

    /**
     * 根据IP和组织分页查询图谱关系
     */
    @Query(value = "MATCH " +
            "(startNode)-[r1:BELONGS_TO]->(endNode1) " +
            "WHERE startNode.Ip = $ip AND endNode1.name = $org " +
            "RETURN startNode, r1, endNode1 " +
            "SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (startNode)-[r1:BELONGS_TO]->(endNode1) " +
                    "RETURN count(r1)")
    Page<IpNode> findRelationsByIpAndOrg(@Param("ip") String ip,
                                         @Param("org") String org,
                                         @Param("pageNum") Integer pageNum,
                                         @Param("pageSize") Integer pageSize, Pageable pageable);

    /**
     * 查询所有没有BELONGS_TO关系的IP节点
     */
    @Query(value = "MATCH (ip:Ip) " +
            "WHERE NOT EXISTS((ip)-[:BELONGS_TO]->()) " +
            "RETURN ip " +
            "SKIP 0 LIMIT 200",
            countQuery = "MATCH (ip:Ip) " +
                    "WHERE NOT (ip)-[:BELONGS_TO]->() " +
                    "RETURN count(ip)")
    List<IpNode> findIpNodeByNoBelongsTo();


    /**
     * 删除指定IP节点的组织关系
     */
    @Query("MATCH (n:Ip) WHERE n.Ip = $ip " +
            "OPTIONAL MATCH (n)-[r:BELONGS_TO]-(o) " +
            "DELETE r")
    void deleteRelationsByIp(@Param("ip") String ip);

    /**
     * 根据IP查询，BELONGS_TO的关系
     */
    @Query(value = "MATCH " +
            "(startNode)-[r1:BELONGS_TO]->(endNode1) " +
            "WHERE startNode.Ip = $ip " +
            "RETURN startNode, r1, endNode1 ")
    List<IpNode> findOrgByIp(@Param("ip") String ip);

    /**
     * 根据IP查询，BELONGS_TO的关系
     */
    @Query(value = "MATCH " +
            "(startNode)-[r1:BELONGS_TO_ORG_REVERSE]->(endNode1) " +
            "WHERE startNode.Ip = $ip " +
            "RETURN startNode, r1, endNode1 ")
    IpNode findOrgByIp1(@Param("ip") String ip);

    /**
     * 根据ip查询指定ip节点City信息
     */
    @Query(value = "MATCH " +
            "(startNode)-[r2:LOCATED_AT]->(endNode2) " +
            "WHERE startNode.Ip = $ip " +
            "RETURN startNode, r2, endNode2")
    IpNode findCityByIp(@Param("ip") String ip);

    /**
     * 根据IP查询域名
     */
    @Query(value = "MATCH " +
            "(startNode)-[r3:DOMAIN_IS]->(endNode3) " +
            "WHERE startNode.Ip = $ip " +
            "RETURN startNode, r3, endNode3 ")
    List<IpNode> findDomainByIp(@Param("ip") String ip);

    /**
     * 根据IP查询IPC_IS关系
     *
     * @param ip
     */
    @Query(value = "MATCH " +
            "(startNode)-[r1:IpC_IS]->(endNode1) " +
            "WHERE startNode.Ip = $ip " +
            "RETURN startNode, r1, endNode1 ")
    IpNode findIPCByIp(@Param("ip") String ip);

    /**
     * 根据ip查询IP node节点,不是查询关系
     *
     * @param ip
     */
    @Query(value = "MATCH (n:Ip) " +
            "WHERE n.Ip = $ip " +
            "RETURN n ")
    IpNode findIpNodeByIp(@Param("ip") String ip);

    // 删除指定IP的BELONG_TO关系
    @Query("MATCH (n:Ip) WHERE n.Ip = $ip " +
            "OPTIONAL MATCH (n)-[r:BELONGS_TO]-(o) " +
            "DELETE r")
    void deleteRelationsByIpAndBelongsTo(@Param("ip") String ip);

    // 根据org和ip删除BELONGS_TO关系
    @Query("MATCH (n:Ip) WHERE n.Ip = $ip " +
            "OPTIONAL MATCH (n)-[r:BELONGS_TO]-(o) " +
            "WHERE o.name = $org " +
            "DELETE r")
    void deleteRelationsByIpAndOrg(@Param("ip") String ip, @Param("org") String org);


    @Query(value = "MATCH (n:Ip) WHERE n.org IS NULL RETURN n SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (n:Ip) RETURN count(n)")
    Page<IpNode> findIpByOrgIsNull(@Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize, Pageable pageable);


    @Query(value = "MATCH path = (ip:Ip {attackability: 1})<-[*1..3]-(o:Org)\n" +
            "WHERE ANY(node IN nodes(path) WHERE 'Org' IN labels(node)) \n" +
            "AND o.name =  $org \n" +
            "RETURN ip" +
            " SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH path = (ip:Ip {attackability: 1})<-[*1..3]-(o:Org) WHERE ANY(node IN nodes(path) WHERE 'Org' IN labels(node))  AND o.name =  $org RETURN  count(ip)")
    Page<IpNode> findIpByAttackability(@Param("org") String org, @Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize, Pageable pageable);

    @Query(value = "MATCH path = (ip:Ip )<-[*1..3]-(o:Org) WHERE ANY(node IN nodes(path) WHERE 'Org' IN labels(node)) and ip.Ip = $ip RETURN o ")
    List<OrgNode> findOrgByIpNew(@Param("ip") String ip);

    @Query(value = "MATCH (n:Ip) where n.attackability=1 RETURN n SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (n:Ip) where n.predictable=1 RETURN count(n)")
    Page<IpNode> findIpByAttackability(@Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize, Pageable pageable);


    @Query(value = "MATCH (n:Ip) where n.predictable=1 RETURN n SKIP $pageNum * $pageSize LIMIT $pageSize",
            countQuery = "MATCH (n:Ip) where n.predictable=1 RETURN count(n)")
    Page<IpNode> findIpByPredictable(@Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize, Pageable pageable);


    @Query(value = "MATCH path = (ip:Ip {Ip: '210.45.236.13'})<-[*1..3]-(o:Org) WHERE ANY(node IN nodes(path) WHERE 'Org' IN labels(node)) RETURN ip.Ip as Ip_node, o as Org_node, [node IN nodes(path) | node] as all_nodes_in_path, [rel IN relationships(path) | rel] as all_relationships_in_path, length(path) as path_length ORDER BY path_length, o")
    List<CombatQueryInfo> findAttackabilityToOrg(@Param("ip") String ip);

    @Query(value = "MATCH (start_ip:Ip {Ip: $ip})-[:BELONGS_TO]->(ipc:IpC) MATCH (ipc)<-[:BELONGS_TO]-(landmark_ip:Ip) WHERE landmark_ip.landmark_node = 1 MATCH path = (landmark_ip)<-[*1..3]-(o:Org) RETURN start_ip, ipc, landmark_ip, o,[node IN nodes(path) | node] as all_nodes_in_path, [rel IN relationships(path) | rel] as all_relationships_in_path, length(path) as path_length")
    List<Map<String, Object>> findPredictableToOrg(@Param("ip") String ip);


}

